﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Health;

namespace SPHealth.SharePoint.HealthRules.SP.Rules.Availability
{
    public class WarnContentDatabaseBackup : SPHealthAnalysisRule
    {
        
        #region Locals
        private const string MaxDaysBackup = "MaxDaysBackup";
        private const string ruleTitle = "WarnContentDatabaseBackup";

        private const string GET_SQL_BACKUP_INFO_FORMAT =
        @"  SELECT MAX(msdb.dbo.backupset.backup_finish_date) AS last_db_backup_date
            FROM  msdb.dbo.backupset 
            WHERE   msdb.dbo.backupset.type = 'D'  AND msdb.dbo.backupset.database_name = '{0}'";


        private readonly StringBuilder _failingContentDatabases;
        private int _numFailingDatabases;
        private SPHealthAnalysisRuleAutomaticExecutionParameters _executionParameters;
        #endregion

        #region Constructor
        public WarnContentDatabaseBackup()
        {           
            _failingContentDatabases = new StringBuilder();
        }
        #endregion
        
        #region Fields

        public override string Summary
        {
            get { return "One or more Content Databases have not been backed up recently."; }
        }
        
        public override string Explanation
        {
            get 
            { 
                return String.Format("The following {0} Content DBs have not been backed up in the last {1} days:\r\n\r\n{2}",
                                        _numFailingDatabases, common.GetRuleSetting(MaxDaysBackup, ruleTitle), _failingContentDatabases); 
            }
        }

        public override string Remedy
        {
            get { return "Schedule all content DBs for regular backups."; }
        }

        public override SPHealthCheckErrorLevel ErrorLevel
        {
            get { return SPHealthCheckErrorLevel.Warning; }
        }

        public override SPHealthCategory Category
        {
            get { return SPHealthCategory.Availability; }
        }
                
        public override SPHealthAnalysisRuleAutomaticExecutionParameters AutomaticExecutionParameters
        {
            get
            {
                if (_executionParameters == null)
                {
                    _executionParameters = new SPHealthAnalysisRuleAutomaticExecutionParameters
                    {
                        Schedule = SPHealthCheckSchedule.Daily,
                        Scope = SPHealthCheckScope.Any,
                        ServiceType = typeof(SPTimerService), //Tell SP if we want our own timer job or can we run with others...
                        RepairAutomatically = false
                    };
                }
                return _executionParameters;
            }
        }
        
        #endregion

        #region Methods

        // used to provide the configuration list item title
        public static string GetTitle()
        {
            // return the title for the rule
            return ruleTitle;
        }

        // used to provide the default configuration settings
        public static IDictionary<string,string> GetDefaultSettings()
        {
            // return the default settings for the rule
            IDictionary<string,string> settings = new Dictionary<string,string>();
            settings.Add("Comment1", "Enter the maximum number of days that can elapse before a warning");
            settings.Add("Comment2", "is raised to indicate that a content database backup is required.");
            settings.Add(MaxDaysBackup, "14");
            return settings;
        }

        public override SPHealthCheckStatus Check()
        {
            if (!SPFarm.Joined)
            {
                throw new InvalidOperationException();
            }
            
            SPHealthCheckStatus status = SPHealthCheckStatus.Passed;

            foreach (SPWebApplication webApplication in SPWebService.ContentService.WebApplications)
            {
                if (webApplication.Status == SPObjectStatus.Online)
                {
                    foreach (SPContentDatabase contentDatabase in webApplication.ContentDatabases)
                    {
                        try
                        {
                            DateTime lastContentDatabaseBackup = GetContentDatabaseLastBackupDate(contentDatabase);
                            if (lastContentDatabaseBackup == DateTime.MaxValue ||
                                DateTime.Now.Subtract(lastContentDatabaseBackup).TotalDays > Convert.ToInt32(common.GetRuleSetting(MaxDaysBackup, ruleTitle)))
                            {
                                status = SPHealthCheckStatus.Failed;
                                _numFailingDatabases++;
                                _failingContentDatabases.AppendLine(String.Format("{0} ({1}) ", 
                                                                contentDatabase.Name,
                                                                lastContentDatabaseBackup == DateTime.MaxValue ? "never" :  lastContentDatabaseBackup.ToShortDateString()));
                            }
                        }
                        catch { /*Best effort*/ }
                    }
                }
            }

            return status;
        }

        private static DateTime GetContentDatabaseLastBackupDate(SPContentDatabase contentDatabase)
        {

            DateTime lastBackupTime = DateTime.MaxValue;
            
            using (SqlConnection connection = new SqlConnection(contentDatabase.DatabaseConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand { Connection = connection, CommandText = String.Format(GET_SQL_BACKUP_INFO_FORMAT, contentDatabase.Name) })
                {

                    object temp = command.ExecuteScalar();
                    if (temp != null && temp != DBNull.Value && temp is DateTime)
                    {
                        lastBackupTime = (DateTime)temp;
                    }
                }
            }

            return lastBackupTime;

        }
        #endregion
    
    }
}
